 
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <CL/cl.h>
#include <sys/time.h>
#include <float.h>
////////////////////////////////////////////////////////////////////////////////
 
// Use a static data size for simplicity
//
#define DATA_SIZE (1)
 	
////////////////////////////////////////////////////////////////////////////////
 
// a kernel that does nothing
//
const char *KernelSource = "\n" \
"__kernel void square(__global float* a)                                                     \n" \
"{                                                                  \n" \
"}                                                                      \n" \
"\n";
 
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
    int err;                            // error code returned from api calls
      
    
    size_t global;                      // global domain size for our calculation
    size_t local;                       // local domain size for our calculation
 
    cl_device_id device_id;             // compute device id 
    cl_platform_id platform;		// compute platform id
    cl_context context;                 // compute context
    cl_command_queue commands;          // compute command queue
    cl_program program;                 // compute program
    cl_kernel kernel;                   // compute kernel
    
    double result = 0;
	int size = atoi(argv[1]);
	int i;
	struct timeval tim;
	double starttime, endtime;

	    float* a = malloc(sizeof(float)*size);
	    for (i=0; i<size;i++)
	    {
		a[i] =  (float)rand()/(float)(RAND_MAX/FLT_MAX);	

	    }
	    // Connect to a compute device
	    //
	    int gpu = 1;
	    err = clGetPlatformIDs(1, &platform, NULL);
	    if (err != CL_SUCCESS)
	    {
	        printf("Error: Failed to select paltform!\n");
	        return 1;
	    }

	    err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL);
	    if (err != CL_SUCCESS)
	    {
	        printf("Error: Failed to select device!\n");
	        return 1;
	    }
	
	    // Create a compute context 
	    //
	    context = clCreateContext(0, 1, &device_id, NULL, NULL, &err);
	    if (!context)
	    {
	        printf("Error: Failed to create a compute context!\n");
	        return 1;
	    }
	 
	    // Create a command queue
	    //
	    commands = clCreateCommandQueue(context, device_id, 0, &err);
	    if (!commands)
	    {
	        printf("Error: Failed to create a command queue!\n");
	        return 1;
	    }

	    // Create the compute program from the source buffer
	    //
	    program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err);
	    if (!program)
	    {
	        printf("Error: Failed to create compute program!\n");
	        return 1;
	    }
	 
	    // Build the program executable
	    //
	    err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
	    if (err != CL_SUCCESS)
	    {
	        size_t len;
	        char buffer[2048];
	 
	        printf("Error: Failed to build program executable!\n");
	        clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len);
	        printf("%s\n", buffer);
	        exit(1);
	    }
	 
	    // Create the compute kernel in the program we wish to run
	    //
	    kernel = clCreateKernel(program, "square", &err);
	    if (!kernel || err != CL_SUCCESS)
	    {
	        printf("Error: Failed to create compute kernel!\n");
	        exit(1);
	    }

   	gettimeofday(&tim, NULL); 
	starttime =  tim.tv_sec*1000+(tim.tv_usec*0.001);
	   	

	    result += endtime - starttime;


	    cl_mem ab = clCreateBuffer(context,  CL_MEM_READ_WRITE,  sizeof(float) * size, NULL, NULL);

	    if (!ab)
	    {
	        printf("Error: Failed to allocate device memory!\n");
	        exit(1);
	    }    

	    // Write our data set into the input array in device memory 
	    //
	    err = clEnqueueWriteBuffer(commands, ab, CL_TRUE, 0, sizeof(float) * size, a, 0, NULL, NULL);
	    if (err != CL_SUCCESS)
	    {
	        printf("Error: Failed to write to source array!\n");
	        exit(1);
	    }

	    // Set the arguments to our compute kernel
	    //
	    
    	    err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &ab);
	    if (err != CL_SUCCESS)
	    {
	        printf("Error: Failed to set kernel arguments! %d\n", err);
	        exit(1);
	    }
	    
	 	global = 1;
	 	local = 1;
	    err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0, NULL, NULL);
	    if (err)
	    {
	        printf("Error: Failed to execute kernel!\n");
	        return 1;
	    }
	 
	    // Wait for the command commands to get serviced before reading back results
	    //
	    clFinish(commands);
	 
	    // Read back the res from the device to verify the output
	    //
	    err = clEnqueueReadBuffer( commands, ab, CL_TRUE, 0, sizeof(float) * size, a, 0, NULL, NULL );  
	    if (err != CL_SUCCESS)
	    {
	        printf("Error: Failed to read output array! %d\n", err);
	        exit(1);
	    }
	gettimeofday(&tim, NULL); 
	endtime  =  tim.tv_sec*1000+(tim.tv_usec*0.001);
	    // Shutdown and cleanup
	    //
	    clReleaseMemObject(ab);
	    clReleaseProgram(program);
	    clReleaseKernel(kernel);
	    clReleaseCommandQueue(commands);
	    clReleaseContext(context);
	    free(a);
	result = endtime-starttime;
	    
	
	//printf( "Average overhead of calling into OpenCL over %d iterations: %f ms \n", Loops, result);
	printf ("%f %f", result, result/size);
}
